feat(snapshots): Add endpoint for downloading images zip#115337
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
🚨 Warning: This pull request contains Frontend and Backend changes! It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently. Have questions? Please ask in the |
📊 Type Coverage Diff✅ No new type safety issues introduced. Coverage: 93.47% |
367ae5e to
1ebb99a
Compare
1ebb99a to
677bcff
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e990e2b. Configure here.
5db78fd to
1c91f37
Compare
1c91f37 to
f9b8a7e
Compare
f9b8a7e to
80054f9
Compare
Screenshots: <img width="688" height="800" alt="image" src="https://github.com/user-attachments/assets/9a234440-405a-428f-b5fe-a8969e94e359" /> <img width="1274" height="1842" alt="image" src="https://github.com/user-attachments/assets/60e7b20d-45a9-465c-81fd-504ca8f4b931" /> ## Summary Frontend companion to #115337. Adds a "Download Images" action to the snapshot header dropdown menu that fetches the zip from the new download endpoint and triggers a browser download. Also cleans up the `SnapshotImage` type and test fixtures: - Removes `content_hash` from the frontend `SnapshotImage` interface — it's an internal storage detail not needed by the UI - Stops filtering `content_hash` from the metadata copy-to-clipboard JSON (since the field is gone) - Filters out `null` values from the metadata JSON to reduce noise - Reorders dropdown menu items: Build Metadata → View Base Build → Download Images → Rerun Status Checks → Delete ## Test plan - [ ] Open a snapshot detail page and verify the dropdown menu shows the new "Download Images" option - [ ] Click "Download Images" and confirm the zip downloads with the correct filename - [ ] Verify the metadata copy button no longer includes `content_hash` or null fields - [ ] Confirm existing snapshot stories still render correctly without `content_hash`
## Summary - Adds a new streaming download endpoint (`/preprodartifacts/snapshots/<id>/download/`) that bundles all snapshot images into a zip file. Images are fetched concurrently in batches (32 workers, 200 per batch) and deduplicated by content hash before zipping. The response is streamed as a `StreamingHttpResponse` — chunks are yielded as each batch is zipped, so the client starts receiving data immediately without buffering the entire archive in memory. - This follows the same streaming download pattern already used by the existing size build artifact download endpoint (`project_preprod_artifact_download.py`), which also uses `StreamingHttpResponse` for large file downloads. - Uses a non-seekable `_DrainableBuffer` so `zipfile` writes data descriptors (no seeking back to update headers), enabling true streaming output. - Replaces the fragile `**dict()` spread pattern in `SnapshotImageResponse` construction with explicit field assignments (`description`, `tags`). The old approach silently forwarded unknown fields via `**kwargs` filtering, which was brittle and hard to follow. ## Test plan - [ ] Hit the download endpoint for an existing snapshot and verify the returned zip contains the expected image files - [ ] Verify 404 is returned for non-existent snapshot IDs - [ ] Verify 403 is returned when the feature flag is disabled - [ ] Confirm that description and tags fields are still returned correctly in the snapshot detail response
Screenshots: <img width="688" height="800" alt="image" src="https://github.com/user-attachments/assets/9a234440-405a-428f-b5fe-a8969e94e359" /> <img width="1274" height="1842" alt="image" src="https://github.com/user-attachments/assets/60e7b20d-45a9-465c-81fd-504ca8f4b931" /> ## Summary Frontend companion to #115337. Adds a "Download Images" action to the snapshot header dropdown menu that fetches the zip from the new download endpoint and triggers a browser download. Also cleans up the `SnapshotImage` type and test fixtures: - Removes `content_hash` from the frontend `SnapshotImage` interface — it's an internal storage detail not needed by the UI - Stops filtering `content_hash` from the metadata copy-to-clipboard JSON (since the field is gone) - Filters out `null` values from the metadata JSON to reduce noise - Reorders dropdown menu items: Build Metadata → View Base Build → Download Images → Rerun Status Checks → Delete ## Test plan - [ ] Open a snapshot detail page and verify the dropdown menu shows the new "Download Images" option - [ ] Click "Download Images" and confirm the zip downloads with the correct filename - [ ] Verify the metadata copy button no longer includes `content_hash` or null fields - [ ] Confirm existing snapshot stories still render correctly without `content_hash`


Summary
/preprodartifacts/snapshots/<id>/download/) that bundles all snapshot images into a zip file. Images are fetched concurrently in batches (32 workers, 200 per batch) and deduplicated by content hash before zipping. The response is streamed as aStreamingHttpResponse— chunks are yielded as each batch is zipped, so the client starts receiving data immediately without buffering the entire archive in memory.project_preprod_artifact_download.py), which also usesStreamingHttpResponsefor large file downloads._DrainableBuffersozipfilewrites data descriptors (no seeking back to update headers), enabling true streaming output.**dict()spread pattern inSnapshotImageResponseconstruction with explicit field assignments (description,tags). The old approach silently forwarded unknown fields via**kwargsfiltering, which was brittle and hard to follow.Test plan